package environment;

import org.newdawn.slick.*;
import org.newdawn.slick.geom.Line;

public class Mobile {
	public static final float GRAVITY = 9.8f * (Dirt.GRID_SCALE / 1.5f) / (1000f / Dirt.INTERVAL) / 2;
	
	private float x, y, w, h;
	
	int doMoveCount = 0;
	
	private float moveRight = 0f, moveDown = 0f;
	private float vVelocity = 0f;
	private float hVelocity = 0f;
	private float[] pointX = new float[3];
	private float[] pointY = new float[3];
	private float[] trajX = new float[3];
	private float[] trajY = new float[3];
	private boolean[] stopX = new boolean[3];
	private boolean[] stopY = new boolean[3];
	private int shortest = -1;
	private float length = Float.MAX_VALUE;
	
	private Dirt dirt;

	public Mobile(GameContainer gc, Dirt dirt, int x, int y, int width, int height) throws SlickException {
		this.x = x;
		this.y = y;
		w = width;
		h = height;
		this.dirt = dirt;

		System.out.println("GRAVITY: " + GRAVITY);
	}
	
	public void update(GameContainer gc, int timePassed, Dirt dirt) throws SlickException {
		float timeR = timePassed / Dirt.INTERVAL;
		float grav = GRAVITY * timeR;
		
		// Gravity
		vVelocity = Math.min(vVelocity + grav, Dirt.GRID_SCALE);
		float hGo = hVelocity * timeR + moveRight;
		float vGo = vVelocity* timeR + moveDown ;
		doMoveCount = 0;
		doMove(hGo, vGo);
		moveRight = 0;
		moveDown = 0;
		/*y += vGo;
		if (vVelocity <= 0) {
			int gridX = (int)x / Dirt.GRID_SCALE;
			int gridX2 = (int)(x + w) / Dirt.GRID_SCALE;
			int gridY = (int)y / Dirt.GRID_SCALE;
			if (!dirt.isMovable(gridX, gridY) || !dirt.isMovable(gridX2, gridY)) {
				y = ((gridY + 1) * Dirt.GRID_SCALE) + 1;
				vVelocity = 0;
			}
		} else {
			int gridX = (int)x / Dirt.GRID_SCALE;
			int gridX2 = (int)(x + w) / Dirt.GRID_SCALE;
			int gridY = (int)(y + h + 1) / Dirt.GRID_SCALE;
			if (!dirt.isMovable(gridX, gridY) || !dirt.isMovable(gridX2, gridY)) {
				y = gridY * Dirt.GRID_SCALE - h - 1;
				vVelocity = 0;
			}
		}*/
	}
	
	void checkCollision (int i, int checkRight, int checkDown, float right, float down) {
		assert -1 <= checkRight && checkRight <= 1 && -1 <= checkDown && checkDown <= 1;
		assert checkRight != 0 || checkDown != 0;
		
		dirt.debugLines.add(new Line(pointX[i], pointY[i], pointX[i] + right, pointY[i] + down));
		
		if (checkRight == 1) {
			if ((pointX[i] % Dirt.GRID_SCALE) + right < Dirt.GRID_SCALE) return;
		}
		if (checkRight == -1){
			if ((pointX[i] % Dirt.GRID_SCALE) + right > 0) return;
		}
		if (checkDown == 1) {
			if ((pointY[i] % Dirt.GRID_SCALE) + down < Dirt.GRID_SCALE) return;
		}
		if (checkDown == -1){
			if ((pointY[i] % Dirt.GRID_SCALE) + down > 0) return;
		}
		int gridX = (int)pointX[i] / Dirt.GRID_SCALE + checkRight;
		int gridY = (int)pointY[i] / Dirt.GRID_SCALE + checkDown;
		if (!dirt.isMovable(gridX, gridY)) {
			float newLength = 0;
			float dX = right;
			float dY = down;
			if (checkRight != 0) {
				dX = (gridX + 0.5f + checkRight * -0.5f) * Dirt.GRID_SCALE - checkRight - pointX[i];
				dY = (dX / right) * down;
				newLength = dX * dX + dY * dY;
				stopX[i] = true;
				stopY[i] = false;
			}
			if (checkDown != 0) {
				dY = (gridY + 0.5f + checkDown * -0.5f) * Dirt.GRID_SCALE - checkDown - pointY[i];
				dX = (dY / down) * right;
				float newNewLength = dX * dX + dY * dY;
				if (newNewLength >= newLength) {
					newLength = newNewLength;
					stopX[i] = false;
					stopY[i] = true;
				}
			}
			if (newLength < length) {
				length = newLength;
				shortest = i;
				trajX[i] = dX;
				trajY[i] = dY;
			}
		}
	}
	
	private void doMove(float right, float down) {
		doMoveCount++;
		shortest = 0;
		length = right * right + down * down;
		trajX[0] = right;
		trajY[0] = down;
		stopX[0] = false;
		stopY[0] = false;
		
		if (right > 0) {
			pointX[0] = x + w;
			pointY[0] = y;
			checkCollision(0, 1, 0, right, down);
			pointX[1] = x + w;
			pointY[1] = y + h;
			checkCollision(1, 1, 0, right, down);
			if (down > 0) {
				checkCollision(0, 0, 1, right, down);
				checkCollision(0, 1, 1, right, down);
				checkCollision(1, 0, 1, right, down);
				checkCollision(1, 1, 1, right, down);
				pointX[2] = x;
				pointY[2] = y + h;
				checkCollision(2, 1, 0, right, down);
				checkCollision(2, 0, 1, right, down);
				checkCollision(2, 1, 1, right, down);
				
			} else if (down < 0) {
				checkCollision(0, 0, -1, right, down);
				checkCollision(0, 1, -1, right, down);
				checkCollision(1, 0, -1, right, down);
				checkCollision(1, 1, -1, right, down);
				pointX[2] = x;
				pointY[2] = y;
				checkCollision(2, 1, 0, right, down);
				checkCollision(2, 0, -1, right, down);
				checkCollision(2, 1, -1, right, down);
			}
		} else if (right < 0) {
			pointX[0] = x;
			pointY[0] = y;
			checkCollision(0, -1, 0, right, down);
			pointX[1] = x;
			pointY[1] = y + h;
			checkCollision(1, -1, 0, right, down);
			if (down > 0) {
				checkCollision(0, 0, 1, right, down);
				checkCollision(0, -1, 1, right, down);
				checkCollision(1, 0, 1, right, down);
				checkCollision(1, -1, 1, right, down);
				pointX[2] = x + w;
				pointY[2] = y + h;
				checkCollision(2, -1, 0, right, down);
				checkCollision(2, 0, 1, right, down);
				checkCollision(2, -1, 1, right, down);
			} else if (down < 0) {
				checkCollision(0, 0, -1, right, down);
				checkCollision(0, -1, -1, right, down);
				checkCollision(1, 0, -1, right, down);
				checkCollision(1, -1, -1, right, down);
				pointX[2] = x + w;
				pointY[2] = y;
				checkCollision(2, -1, 0, right, down);
				checkCollision(2, 0, -1, right, down);
				checkCollision(2, -1, -1, right, down);
			}
		} else {
			if (down > 0) {
				pointX[0] = x;
				pointY[0] = y + h;
				checkCollision(0, 0, 1, right, down);
				pointX[1] = x + w;
				pointY[1] = y + h;
				checkCollision(1, 0, 1, right, down);
			} else if (down < 0) {
				pointX[0] = x;
				pointY[0] = y;
				checkCollision(0, 0, -1, right, down);
				pointX[1] = x + w;
				pointY[1] = y;
				checkCollision(1, 0, -1, right, down);
			} else {
				return;
			}
		}
		
		if (stopX[shortest]) {
			right = 0;
			hVelocity = 0;
		} else {
			right -= trajX[shortest];
		}
		if (stopY[shortest]) {
			down = 0;
			//System.out.println("vVelocity: " + vVelocity);
			vVelocity = 0;
		} else {
			down -= trajY[shortest];
		}
		
		x += trajX[shortest];
		y += trajY[shortest];
		
		doMove(right, down);
	}
			
	public int getX() {
		return (int)x;
	}

	public int getY() {
		return (int)y;
	}

	public int getRight() {
		return (int)(x + w);
	}

	public int getBottom() {
		return (int)(y + h);
	}

	public float getVVelocity() {
		return vVelocity;
	}

	void addVVelocity(float value) {
		vVelocity += value;
	}

	public void move(float right, float down) {
		moveRight += right;
		moveDown += down;
	}
}
